<?php

namespace App\Exceptions;

use App\Custom\Api\ApiResponse;
use Exception;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Validation\ValidationException;
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Throwable;

class CustomExceptionHandler
{

    use ApiResponse;

    /**
     * @var Exception
     */
    public $exception;
    /**
     * @var Request
     */
    public $request;

    /**
     * @var
     */
    protected $response;

    /**
     * ExceptionReport constructor.
     * @param Request $request
     * @param Throwable $exception
     */
    function __construct(Request $request, Throwable $exception)
    {
        $this->request = $request;
        $this->exception = $exception;
    }

    /**
     * @return bool
     */
    public function shouldReturn()
    {
        if (!($this->request->wantsJson() || $this->request->ajax())) {
            return false;
        }

        foreach (array_keys($this->doResponse) as $response) {
            if ($this->exception instanceof $response) {
                $this->response = $response;
                return true;
            }
        }

        // 没设定的异常都统一输出默认信息
        $this->response = 'DEFAULT';
        return true;
    }

    /**
     * @var array
     */
    public $doResponse = [
        AuthenticationException::class => ['身份验证失败', 401],
        UnauthorizedHttpException::class => ['身份未授权', 401],
        ModelNotFoundException::class => ['该模型未找到', 404],
        NotFoundHttpException::class => ['内容不存在', 404],
        DecryptException::class => ['参数错误', 409],
        ValidationException::class => ['数据验证失败', 422],
        MethodNotAllowedHttpException::class => ['方法不存在', 404],
        NotAcceptableHttpException::class => ['禁止操作', 403],
        ConditionException::class => [],
    ];

    /**
     * @param Throwable $e
     * @return static
     */
    public static function make(Throwable $e)
    {
        return new static(\request(), $e);
    }

    /**
     * @return mixed
     */
    public function response()
    {
        // 默认异常处理
        if ($this->response == 'DEFAULT') {
            list($message, $statusCode, $extend) = $this->responseDefaultException();

        } else {
            $reportArray = $this->doResponse[$this->response];
            $message = $this->exception->getMessage() ?: $reportArray[0];
            $statusCode = method_exists($this->exception, 'getStatusCode') ? $this->exception->getStatusCode() : $reportArray[1];
            // 某些异常需要输出多一个扩展信息
            $extend = $this->extendExceptionResponse();
        }

        $meta = $this->getMetaFromException();

        return $this->meta($meta)->failed($message, $statusCode, $extend);
    }

    /**
     * 统一处理一些需要额外字段返回的异常
     */
    protected function extendExceptionResponse()
    {
        $extend = [];

        // 处理表单验证异常
        if ($this->exception instanceof ValidationException) {
            $extend['errors'] = $this->exception->errors();
        }

        return $extend;
    }

    /**
     * 处理meta
     * @return array
     */
    protected function getMetaFromException()
    {
        $meta = [];

        if (method_exists($this->exception, 'getMeta')) {
            $meta = $this->exception->getMeta();
        }

        return $meta;
    }

    /**
     * 统一输出默认异常
     */
    protected function responseDefaultException()
    {
        $message = $this->exception->getMessage();
        $statusCode = 500;

        $exception = Str::snake(class_basename($this->exception));
        $extend = ['exception' => $exception];

        return [$message, $statusCode, $extend];
    }

}
